package edu.uwm.RobberKnight.Model;

import java.awt.Color;
import java.awt.Point;
import java.util.*;

public class Board implements java.io.Serializable {


	private static final long serialVersionUID = 4904716659358997004L;
	public static int MAX_PLAYABLE_KNIGHTS = 5;
    private int _boundary;
    private int _playableKnights;
    private int _playableTiles;
    private Tile[][] _tiles;
    private RobberKnightsGame _game;
    private Tile _lastPlacedTile;
    private int _leftBoundary;
    private int _rightBoundary;
    private int _topBoundary;
    private int _botomBoundary;
    private boolean _haveDirection;
    private String _direction;//_direction can be left, right, up or down. So maybe it's better to define it as an enum.
    private List<Point> _lstUsedTiles;

    protected Board(RobberKnightsGame game) {
        _haveDirection = false;
        _direction = "";
        _lstUsedTiles = new LinkedList<Point>();
        _game = game;
        setBoundry();
        nextTurn();
    }

    protected void setBoundry() {
        int playerSize = _game.getPlayers().size();
        switch (playerSize) {
            case 2:
                _boundary = 7;
                _tiles = new Tile[12][12];
                break;
            case 3:
                _boundary = 9;
                _tiles = new Tile[15][16];
                break;
            case 4:
                _boundary = 10;
                _tiles = new Tile[16][18];
                break;
        }
        _leftBoundary = _boundary - playerSize;
        _rightBoundary = _boundary - 1;
        _topBoundary = _boundary - 2;
        _botomBoundary = _boundary - 1;
    }

    /*
     * Returns how many knights may yet be played this turn.
     */
    public int getPlayableKnights() {
        return _playableKnights;
    }

    /*
     * Return the tiles that the player can legally place knights on.
     */
    public LinkedList<Tile> getValidMoves() {

        LinkedList<Tile> retval = new LinkedList<Tile>();
        if (!isKnightsPlayable()) {
            return retval;
        }

        if (_lastPlacedTile.getKnights().size() < _lastPlacedTile.getMinDefaultKnight()) {
            retval.add(_tiles[_lastPlacedTile.xPosition()][_lastPlacedTile.yPosition()]);
        } else if (!_haveDirection) {
            int x = _lastPlacedTile.xPosition();
            int y = _lastPlacedTile.yPosition();

            if (_tiles[x][y].getKnights().size() < Tile.MAX_KNIGHTS) {
                retval.add(_tiles[x][y]);
            }

            if (x <= _tiles.length - 2) {
                if (_tiles[x + 1][y] != null) {
                    if (checkValidKnights(x + 1, y)) {
                        retval.add(_tiles[x + 1][y]);
                    }
                }
            }

            if (x >= 1) {
                if (_tiles[x - 1][y] != null) {
                    if (checkValidKnights(x - 1, y)) {
                        retval.add(_tiles[x - 1][y]);
                    }
                }
            }

            if (y <= _tiles[0].length - 2) {
                if (_tiles[x][y + 1] != null) {
                    if (checkValidKnights(x, y + 1)) {
                        retval.add(_tiles[x][y + 1]);
                    }
                }
            }

            if (y >= 1) {
                if (_tiles[x][y - 1] != null) {
                    if (checkValidKnights(x, y - 1)) {
                        retval.add(_tiles[x][y - 1]);
                    }
                }
            }
        } else {
            for (Point point : _lstUsedTiles) {
                if (checkValidKnights(point.x, point.y)) {
                    retval.add(_tiles[point.x][point.y]);
                }
            }

            if (_direction.equals("left")) {
                int minX = _lastPlacedTile.xPosition();
                for (Point point : _lstUsedTiles) {
                    if (point.x < minX) {
                        minX = point.x;
                    }
                }
                if (minX >= 1) {
                    if (_tiles[minX - 1][_lastPlacedTile.yPosition()] != null) {
                        if (checkValidKnights(minX - 1, _lastPlacedTile.yPosition())) {
                            retval.add(_tiles[minX - 1][_lastPlacedTile.yPosition()]);
                        }
                    }
                }
            }

            if (_direction.equals("right")) {
                int maxX = _lastPlacedTile.xPosition();
                for (Point point : _lstUsedTiles) {
                    if (point.x > maxX) {
                        maxX = point.x;
                    }
                }
                if (maxX <= _tiles.length - 2) {
                    if (_tiles[maxX + 1][_lastPlacedTile.yPosition()] != null) {
                        if (checkValidKnights(maxX + 1, _lastPlacedTile.yPosition())) {
                            retval.add(_tiles[maxX + 1][_lastPlacedTile.yPosition()]);
                        }
                    }
                }
            }

            if (_direction.equals("up")) {
                int minY = _lastPlacedTile.yPosition();
                for (Point point : _lstUsedTiles) {
                    if (point.y < minY) {
                        minY = point.y;
                    }
                }
                if (minY >= 1) {
                    if (_tiles[_lastPlacedTile.xPosition()][minY - 1] != null) {
                        if (checkValidKnights(_lastPlacedTile.xPosition(), minY - 1)) {
                            retval.add(_tiles[_lastPlacedTile.xPosition()][minY - 1]);
                        }
                    }
                }
            }

            if (_direction.equals("down")) {
                int maxY = _lastPlacedTile.yPosition();
                for (Point point : _lstUsedTiles) {
                    if (point.y > maxY) {
                        maxY = point.y;
                    }
                }
                if (maxY <= _tiles[0].length - 2) {
                    if (_tiles[_lastPlacedTile.xPosition()][maxY + 1] != null) {
                        if (checkValidKnights(_lastPlacedTile.xPosition(), maxY + 1)) {
                            retval.add(_tiles[_lastPlacedTile.xPosition()][maxY + 1]);
                        }
                    }
                }
            }
        }

        return retval;
    }

    /*
     * Checks if the Tile is valid candidate to place knights on it or not.
     */
    private boolean checkValidKnights(int x, int y) {
        if (_tiles[x][y].getTileType() == TileType.LAKE) {
            return false;
        } else if (_tiles[x][y].getKnights().size() < Tile.MAX_KNIGHTS) {
            if (_lstUsedTiles.contains(new Point(x, y))) {
                if (_playableKnights >= 1) {
                    return true;
                }
            } else {
                if (_tiles[x][y].getTileType() == TileType.PLAIN || _tiles[x][y].getTileType() == TileType.CASTLE_PLAIN || _tiles[x][y].getTileType() == TileType.TOWN_PLAIN || _tiles[x][y].getTileType() == TileType.VILLAGE_PLAIN) {
                    if (_tiles[x][y].getKnights().size() <= Tile.MAX_KNIGHTS - 1 && _playableKnights >= 1) {
                        return true;
                    }
                } else if (_tiles[x][y].getTileType() == TileType.CASTLE_FOREST || _tiles[x][y].getTileType() == TileType.TOWN_FOREST || _tiles[x][y].getTileType() == TileType.VILLAGE_FOREST) {
                    if (_tiles[x][y].getKnights().size() <= Tile.MAX_KNIGHTS - 2 && _playableKnights >= 2) {
                        return true;
                    }
                } else if (_tiles[x][y].getTileType() == TileType.MOUNTAIN) {
                    if (_tiles[x][y].getKnights().size() <= Tile.MAX_KNIGHTS - 2 && _playableKnights >= 3) {
                        return true;
                    }
                }
            }

        }
        return false;
    }
    
    /*
     * returns minimum number of knights that should be placed on a tile. 
     */
    public int getMinKnight(Tile tile) {
        if (tile.getTileType() == TileType.LAKE) {
            return 0;
        } else {
            if (_lstUsedTiles.contains(new Point(tile.xPosition(), tile.yPosition()))) {
                return 1;
            } else {
                return tile.getMinDefaultKnight();
            }
        }
    }

    /*
     * getValidLocs	Returns a collection of coordinate pairs that map to places
     * where tiles may legally be placed.
     */
    public LinkedList<Point> getValidLocs() {

        LinkedList<Point> retval = new LinkedList<Point>();
        if (_game.getState() == GameState.initialTilePlacement) {
            retval = getValidInitialLocs();
        } else {
            for (int x = 0; x < _tiles.length; x++) {
                for (int y = 0; y < _tiles[x].length; y++) {
                    if (_tiles[x][y] == null) {
                        if (x + 1 < _tiles.length) {//check left empty slot
                            if (_tiles[x + 1][y] != null && _rightBoundary - x < _boundary) {
                                retval.add(new Point(x, y));
                                continue;
                            }
                        }
                        if (x - 1 >= 0) {//check right empty slot
                            if (_tiles[x - 1][y] != null && x - _leftBoundary < _boundary) {
                                retval.add(new Point(x, y));
                                continue;
                            }
                        }
                        if (y + 1 < _tiles[x].length) {//check top empty slot
                            if (_tiles[x][y + 1] != null && _botomBoundary - y < _boundary) {
                                retval.add(new Point(x, y));
                                continue;
                            }
                        }
                        if (y - 1 >= 0) {//check bottom empty slot
                            if (_tiles[x][y - 1] != null && y - _topBoundary < _boundary) {
                                retval.add(new Point(x, y));
                            }
                        }
                    }
                }
            }
        }
        return retval;
    }

    /**
     * Returns the tile locations that are initially available in the setup
     * state
     */
    private LinkedList<Point> getValidInitialLocs() {
        LinkedList<Point> retval = new LinkedList<Point>();
        for (int x = _boundary - _game.getPlayers().size(); x < _boundary; x++) {
            for (int y = _boundary - 2; y < _boundary; y++) {
                if (_tiles[x][y] == null) {
                    retval.add(new Point(x, y));
                }
            }
        }
        return retval;
    }

    /**
     * Update the knights direction fields for showing valid knight moves.
     */
    public boolean updateKnightDirections(Tile tile, Player p) {

        if (_lastPlacedTile.getPosition().x != tile.xPosition() || _lastPlacedTile.getPosition().y != tile.yPosition()) {
            _haveDirection = true;
        }

        if (tile.getPosition().x < _lastPlacedTile.xPosition()) {
            _direction = "left";
        } else if (tile.getPosition().x > _lastPlacedTile.xPosition()) {
            _direction = "right";
        } else if (tile.getPosition().y < _lastPlacedTile.yPosition()) {
            _direction = "up";
        } else if (tile.getPosition().y > _lastPlacedTile.yPosition()) {
            _direction = "down";
        }

        _game.print(tile.getPlayer().getName() + " placed " + tile.getMinDefaultKnight() + " knight(s) at (" + tile.getPosition().x + ", " + tile.getPosition().y + ")");
        this.calcScore();
        return true;
    }

    public boolean lstUsedPointsContains( int x, int y){
        Point point = new Point(x, y);
        if (!_lstUsedTiles.contains(point)) {
            return false;
        } else {
            return true;
        }
    }
    
    public void addLstUsedPoint(int x, int y){
    	_lstUsedTiles.add(new Point(x,y));
    }
    
    public void decrementPlayableKnight(){
    	_playableKnights--;
    }
    /*
     * Places a tile at the requested location. Returns whether the tile was
     * successfully placed.
     */
    public boolean placeTile(Tile tile) {
        if (_playableTiles > 0 && isValidTilePlacement(tile.xPosition(), tile.yPosition()) && tile.getPlayer() == _game.getCurrentPlayer()) {
            _haveDirection = false;
            _lstUsedTiles.clear();
            _direction = "";
            _tiles[tile.xPosition()][tile.yPosition()] = tile;
            _game.print(tile.getPlayer().getName() + " placed " + tile.getTileType().toString() + " at (" + tile.xPosition() + ", " + tile.yPosition() + ")");
            if (_game.getState() != GameState.initialTilePlacement) {
                _lastPlacedTile = tile;
            }
            if (_game.getState() == GameState.regularPlay && (tile.getTileType() == TileType.CASTLE_FOREST || tile.getTileType() == TileType.CASTLE_PLAIN)) {
                _playableKnights = MAX_PLAYABLE_KNIGHTS;
            } else {
                _playableKnights = 0;
            }
            //If we are in initial tile placement phase and there are no valid locations, proceed to regular game play
            if (_game.getState() == GameState.initialTilePlacement && getValidLocs().size() == 0) {
                _game.proceedToRegularPlay();
            }
            --_playableTiles;
            updateBoundaries(tile.xPosition(), tile.yPosition());
            return true;
        }
        return false;
    }

    /**
     * Keeps track of outer board boundaries
     *
     * @param x
     * @param y
     */
    private void updateBoundaries(int x, int y) {
        if (x < _leftBoundary) {
            _leftBoundary = x;
        }
        if (x > _rightBoundary) {
            _rightBoundary = x;
        }
        if (y < _topBoundary) {
            _topBoundary = y;
        }
        if (y > _botomBoundary) {
            _botomBoundary = y;
        }
    }

    /**
     * Returns whether or not the location is valid for a tile to be placed
     * there
     *
     * @param x
     * @param y
     * @return
     */
    public boolean isValidTilePlacement(int x, int y) {
        for (Point p : getValidLocs()) {
            if (p.x == x && p.y == y) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns a collection of tiles currently on the board
     */
    public LinkedList<Tile> getTiles() {
        LinkedList<Tile> retval = new LinkedList<Tile>();
        for (int x = 0; x < _tiles.length; x++) {
            for (int y = 0; y < _tiles[x].length; y++) {
                if (_tiles[x][y] != null) {
                    retval.add(_tiles[x][y]);
                }
            }
        }
        return retval;
    }

    /**
     * Resets the number of playable tiles available to the player
     */
    public void nextTurn() {
        if (_game.getState() == GameState.initialTilePlacement) {
            _playableTiles = 1;
        } else {
            _playableTiles = 3;
        }
        _lastPlacedTile = null;
    }

    /**
     * Returns the width of a tile
     */
    public int getWidth() {
        return _tiles.length;
    }

    public void setLastPlaced(Tile t) {
    	_lastPlacedTile = t;
    }
    /**
     * Returns the height of a tile
     */
    public int getHeight() {
        return _tiles[0].length;
    }

    /**
     * Returns the number of playable left for the current move
     */
    public int getPlayableTiles() {
        return _playableTiles;
    }

    /**
     * Returns whether or not the current player may place knights
     */
    public boolean isKnightsPlayable() {
        if (_lastPlacedTile == null || _game.getState() == GameState.initialTilePlacement || _playableKnights <= 0) {
            return false;
        }
        if (_lastPlacedTile.isCastle()) {
            return true;
        }
        return false;

    }

    private void calcScore() {
        int p1 = 0;
        int p2 = 0;
        int p3 = 0;
        int p4 = 0;
        for (int i = 0; i < _tiles.length; i++) {
            for (int j = 0; j < _tiles[i].length; j++) {
                if (_tiles[i][j] != null && _tiles[i][j].getKnights() != null && _tiles[i][j].getKnights().size() > 0) {
                    if (_tiles[i][j].getKnights().peek().getPlayer().getColor().equals(Color.red)) {
                        p1 += _tiles[i][j].getScore();
                    } else if (_tiles[i][j].getKnights().peek().getPlayer().getColor().equals(Color.blue)) {
                        p2 += _tiles[i][j].getScore();
                    } else if (_tiles[i][j].getKnights().peek().getPlayer().getColor().equals(Color.green)) {
                        p3 += _tiles[i][j].getScore();
                    } else if (_tiles[i][j].getKnights().peek().getPlayer().getColor().equals(Color.yellow)) {
                        p4 += _tiles[i][j].getScore();
                    }
                }
            }
        }
        for (Player player : _game.getPlayers()) {
            if (player.getColor().equals(Color.red)) {
                player.setScore(p1);
            }
            if (player.getColor().equals(Color.blue)) {
                player.setScore(p2);
            }
            if (player.getColor().equals(Color.green)) {
                player.setScore(p3);
            }
            if (player.getColor().equals(Color.yellow)) {
                player.setScore(p4);
            }
        }
    }
}
